﻿#include "DownloadController.h"
#include "HttpDownloader.h"
#include "FtpDownloader.h"
#include <string>
#include <QNetworkAccessManager>
#include <QEventLoop>
#include <QtCore>
#include <QtNetwork>
#include <QDebug>
#include <QProcess>
#include <QDesktopServices>

 std::mutex  m_mutex;


DownloadController::DownloadController(QObject *parent)
: QObject(parent)
{
        m_DownloadCount = 0;
        m_FinishedNum = 0;
        m_FileSize = 0;
        m_File = new QFile;
}

DownloadController::~DownloadController()
{

}

void responseCode()
{

}

//用阻塞的方式获取下载文件的长度
qint64 DownloadController::GetFileSize(QUrl url)
{

            if(!isNetWorkOnline())
            {
                QString ret("错误：请连接网络");
                emit error(ret);
                return -1;
             }
            QNetworkAccessManager manager;
            QNetworkRequest request(url);

            qDebug() << "Getting the file size...";

            m_Scheme = url.scheme();
    if(m_Scheme == QLatin1String("https") || m_Scheme == QLatin1String("http")){

           //发出请求，获取目标地址的头部信息
            QNetworkReply *reply = manager.head(QNetworkRequest(url));
            QEventLoop loop;
            QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()), Qt::DirectConnection);
            loop.exec();

            QVariant var = reply->header(QNetworkRequest::ContentLengthHeader);
            QString fileSize = reply->rawHeader(QString("Content-Length").toUtf8());
            std::int64_t size = fileSize.toLongLong();
            reply->deleteLater();
            //判断初始化的连接获取文件大小是否成功
            if(size <= 0)
            {
                QString ret("错误：请输入正确的http网址");
                emit error(ret);
                return -1;
            }

            return size;
    }

    if(m_Scheme == QLatin1String("ftp"))
    {
        QFileInfo fileInfo(m_Url.path());
               QString fileName = fileInfo.fileName();

               QString getSizeStr = "SIZE /" + fileName + "\r\n";
               QByteArray ba = getSizeStr.toLocal8Bit();
               //TcpSocket链接
                m_tcpSocket = new QTcpSocket;
                m_tcpSocket->connectToHost(m_Url.host(), m_Url.port());
                //qDebug()<<"GetFileSize::port"<<m_Url.port();
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                m_tcpSocket->waitForReadyRead();
                QByteArray tmp;
                tmp = m_tcpSocket->readAll();
                //验证账号密码
                std::string userStr = "USER " +m_Url.userName().toStdString() +"\r\n";
                m_tcpSocket->write(userStr.c_str());
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                m_tcpSocket->waitForReadyRead();
                tmp = m_tcpSocket->readAll();
                std::string pwdStr = "PASS " +m_Url.password().toStdString() +"\r\n";
                m_tcpSocket->write(pwdStr.c_str());
                std::this_thread::sleep_for(std::chrono::milliseconds(100));

                m_tcpSocket->waitForReadyRead();
                // 读取响应信息
                QString response = m_tcpSocket->readAll();
                qDebug() << response;
                if(response=="")
                {
                    QString ret("错误：请输入正确的ftp网址");
                    emit error(ret);
                    return -1;
                }

                // 解析出响应码
                QString responseCode = response.left(3);
               // 判断响应码
                if (responseCode == "530") {
                   QString ret("错误：账号或密码错误");
                   emit error(ret);
                   return -1;
               }
                else if(responseCode == "230")
                {
                   //连接成功
                   m_tcpSocket->write(ba.data());
                   std::this_thread::sleep_for(std::chrono::milliseconds(100));
                   m_tcpSocket->waitForReadyRead();
                   //接收
                   QByteArray recv = m_tcpSocket->readAll();
                   qDebug() << recv;
                   std::string recvStr(recv.data());
                   std::string recvSize;
                   int temp = recvStr.find("213");
                   if(temp < 0){

                       return -1;
                      }
                   else{
                       recvSize = recvStr.substr(temp + 4);
                   }
                   qint64 size = std::stoi(recvSize);
                   m_tcpSocket->close();
                   qDebug() << "FTP ：The file size is: " << size;
                   delete m_tcpSocket;

                   //返回总文件的字节大小
                   return size;
               }




    }

    return -1;
}


//短链接处理
void DownloadController:: shortLink(const QString& url)
{
      m_Url = QUrl(url);
      if(m_Url.scheme() == QLatin1String("https") || m_Url.scheme() == QLatin1String("http")){
          QNetworkAccessManager manager;
          QNetworkRequest request(m_Url);

          for(int i=0;i<redirectIndex;i++)
          {
              //发出请求，获取目标地址的头部信息
              QNetworkReply *reply = manager.head(QNetworkRequest(m_Url));
              QEventLoop loop;
              QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()), Qt::DirectConnection);
              loop.exec();
              QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
               //判断是否是短链接
              if(!possibleRedirectUrl.isNull()) {
                      QUrl redirectUrl = possibleRedirectUrl.toUrl();
                      if(redirectUrl.isRelative()) {
                          QUrl originalUrl = reply->request().url();
                          redirectUrl = originalUrl.resolved(redirectUrl);
                      }

                      // 重定向后的 URL
                      //qDebug() << "Redirection Target: " << redirectUrl.toString();
                     m_Url=redirectUrl;
                     }
               else
                      break;
           }
      }
}

//判断是否连接到网络
 bool DownloadController::isNetWorkOnline()
{

     QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
         //遍历所有网络接口查询
         for (const QNetworkInterface& iface : interfaces) {
             if (iface.flags().testFlag(QNetworkInterface::IsUp) &&
                 !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) {
                 QList<QNetworkAddressEntry> entries = iface.addressEntries();

                 for (const QNetworkAddressEntry& entry : entries) {
                     if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol &&
                         entry.netmask().toString() != "0.0.0.0") {
                         QHostAddress addr = entry.ip();
                         if (addr != QHostAddress::LocalHost &&
                             addr != QHostAddress::LocalHostIPv6 &&
                             !addr.isNull() &&
                             addr.toString().contains("172.20.")) { // 根据wifi IP更改
                             //qDebug()<<"有网";
                             return true;
                         }
                     }
                 }
             }
         }
         //qDebug()<<"没网";
         return false;

}

//两种协议下的URL端口处理
void DownloadController::setURL(const QString& url, const QString& username, const QString& password)
{
        //QUrl u = QUrl(url);
        m_Scheme = m_Url.scheme();

            {
                m_Url.setPort(80);
            }
        if(m_Scheme == QLatin1String("ftp"))
        {
            m_Url.setPort(21);
            if(!username.isEmpty()){
                m_Url.setUserName(username);
            }else{
                // 匿名登录
                m_Url.setUserName("anonymous");
            }
            if(!password.isEmpty()){
                m_Url.setPassword(password);
            }
        }else if(m_Scheme == QLatin1String("http"))
        {
            m_Url.setPort(80);
        }else if(m_Scheme == QLatin1String("https"))
        {
            m_Url.setPort(443);
        }
   // m_Url = u;
}
//设置下载路径
void DownloadController::setDestPath(const QString filePath)
{
       m_filePath = filePath;
}

void DownloadController::httpConectSlot(HttpDownloader* tempDownload)
{
        //http:开始下载
        connect(this, SIGNAL(startDownload(QUrl, QFile*)), tempDownload, SLOT(onStart(QUrl, QFile*)));

        //http:停止线程下载
        connect(this, SIGNAL(stopThreadTask()), tempDownload, SLOT(onStop()));

        //http:恢复线程下载
        connect(this, SIGNAL(restartThreadTask()), tempDownload, SLOT(onRestart()));

        //http:文件下载完成和删除
        connect(tempDownload, SIGNAL(DownloadFinished(int)), this, SLOT(SubPartFinished(int)));
        connect(tempDownload, SIGNAL(DownloadFinished(int)), tempDownload, SLOT(deleteLater()));
        //http:更新当前文件大小
        connect(tempDownload, SIGNAL(updateCurrentSize(int,int)), this, SLOT(updateCurrentSize(int,int)));

        //http:创建线程
        QThread *downloadThread = new QThread;
        m_ThreadList.push_back(downloadThread);
        m_HttpDownloaderList.push_back(tempDownload);


        //http:检测线程是否完成
        connect(downloadThread, SIGNAL(finished()), downloadThread, SLOT(deleteLater()));
        connect(downloadThread, SIGNAL(finished()), this, SLOT(finshedThread()));

        //http:开启线程工作
        tempDownload->moveToThread(downloadThread);
        downloadThread->start();
        emit startDownload(m_Url, m_File);
}

void DownloadController::ftpConectSlot(FtpDownloader* tempDownload)
{
        //ftp:开始下载
        connect(this, SIGNAL(startDownload(QUrl, QFile* )), tempDownload, SLOT(onStart(QUrl, QFile*)));

        //ftp:停止线程下载
        connect(this, SIGNAL(stopThreadTask()), tempDownload, SLOT(onStop()));

        //ftp:恢复线程下载
        connect(this, SIGNAL(restartThreadTask()), tempDownload, SLOT(onRestart()));

        //ftp:文件下载完成和删除
        connect(tempDownload, SIGNAL(DownloadFinished(int)), this, SLOT(SubPartFinished(int)));
        connect(tempDownload, SIGNAL(DownloadFinished(int)), tempDownload, SLOT(deleteLater()));
        connect(tempDownload, SIGNAL(error(QString)), this, SLOT(onError(QString)));

        //ftp:更新当前文件大小
        connect(tempDownload, SIGNAL(updateCurrentSize(int,int)), this, SLOT(updateCurrentSize(int,int)));

        //ftp:创建线程
        QThread *downloadThread = new QThread;
        m_ThreadList.push_back(downloadThread);
        m_FtpDownloaderList.push_back(tempDownload);

        //ftp:检测线程是否完成下载任务
        connect(downloadThread, SIGNAL(finished()), downloadThread, SLOT(deleteLater()));
        connect(downloadThread, SIGNAL(finished()), this, SLOT(finshedThread()));

        //ftp:开启线程工作
        tempDownload->moveToThread(downloadThread);
        downloadThread->start();
        emit startDownload(m_Url, m_File);

}


void DownloadController::saveFilesize()
{
        std::vector<std::int64_t> count_size;
        for(int i = 0;i< 8; i++)
        {

            //先算出每个线程的下载数据开头和结尾
            int start = m_FileSize * i / m_DownloadCount;
            count_size.push_back(start);
            int end = m_FileSize * (i+1) / m_DownloadCount;
            count_size.push_back(end);
        }
        m_mutex.lock();
        std::ofstream file("C:\\Users\\CHD\\Desktop\\download.txt", std::ios::app); // 打开文件并追加到末尾
         if (file.is_open()) {
          for (int i = 0; i < count_size.size(); i++) {
                   std::string line = QString::number(count_size[i]).toStdString() + "\n";
                   file << line;
               }
               file.close(); // 关闭文件
           }
        m_mutex.unlock();
}
        //连接服务器，开启多线程下载
void DownloadController::startFileDownload(const QString url, int count, int speed, QString userName, QString password)
  {
            m_DownloadCount = count;
            m_FinishedNum = 0;
            //两种协议下的URL端口处理
            setURL(url, userName, password);
            //连接并获取文件大小
            m_FileSize = GetFileSize(m_Url);
            //获得文件的名字

            QFileInfo fileInfo(m_Url.path());
            QString scheme = m_Url.scheme();
            QString fileName = fileInfo.fileName();

            if (fileName.isEmpty())
             fileName = "index.html";//尝试获取index.html
         //      qDebug()<<fileName;
            //创建本地文件
             qDebug()<<"filename"<<m_filePath;
            m_File->setFileName(m_filePath);
            //打开文件
            m_File->open(QIODevice::WriteOnly);
            if(!m_File->isOpen())
            {
                m_Status = t_Error;
                QString ret("FilePath Error");
                emit error(ret);
                return;
            }
            // 每个线程的速度限制
            speed /= count;
            // 读取文件中的上次下载数据
            std::vector<std::string> lines;
            std::ifstream fin(m_outputFile);
            if (fin) {
                std::string line;
                while (getline(fin, line)) {
                    lines.push_back(line);
                }
                fin.close();
            }
            if(lines.size()==4)
            {
            saveFilesize();
            m_nCurrentDownloadSize = 0;
            }
            //将文件分成PointCount段，用异步的方式下载
            //每一个i代表一个线程
            bool isContinue=false;
            if(lines[3]!="0")
            {
                isContinue=true;
            }
            //开启多线程异步下载
            for(int i = 0;i< m_DownloadCount; i++)
            {
                qint64 start,end;

                //先算出每个线程的下载数据开头和结尾
                //断点续下
                if(isContinue)
                {
                    start=std::stoi(lines[i*2+4]);
                    end=std::stoi(lines[i*2+5]);
                }
                else
                {
                 start= m_FileSize * i / m_DownloadCount;
                 end = m_FileSize * (i+1) / m_DownloadCount;
                 if( i != 0 )
                    start++;
                }

                qDebug()<<"i"<<i<<" size "<<end-start<<"start"<<start<<"end"<<end;
                if(m_Scheme == QLatin1String("http") || m_Scheme == QLatin1String("https"))
                {
                    //创建http下载器
                    HttpDownloader *tempDownload = new HttpDownloader(start, end, i+1, speed,nullptr);
                    httpConectSlot(tempDownload);

                }
                else if(m_Scheme == QLatin1String("ftp"))
                {

                    //创建ftp下载器(下载数据字节的开始和结尾)
                    FtpDownloader *tempDownload = new FtpDownloader(start, end, i+1, speed, nullptr);
                    ftpConectSlot(tempDownload);

                }
            }

    }

//删除m_HttpDownloaderList结束的线程下载器
int DownloadController::CountDelectIndex(int m_Index)
{
        int tmp=m_Index;
        for(int num:delect_num)
        {
            if(num<tmp)
                m_Index--;
        }
      delect_num.push_back(tmp);
      return m_Index-1;
}

//完成下载
void DownloadController::SubPartFinished(int m_Index)
{
        //获取删除的索引号
        int size=CountDelectIndex(m_Index);
        //删除Http下载器组中的线程
        if(m_Scheme == QLatin1String("http") || m_Scheme == QLatin1String("https"))
        {
        m_HttpDownloaderList.erase(m_HttpDownloaderList.begin()+size);
        }
        //删除Ftp下载器组中的线程
        else if(m_Scheme == QLatin1String("ftp"))
        {
          m_FtpDownloaderList.erase(m_FtpDownloaderList.begin()+size);
          qDebug()<<"erase"<<m_Index;
        }
        //线程退出
        //m_ThreadList[m_Index-1]->quit();
        //完成线程数加1
        m_FinishedNum++;

        //如果完成数等于文件段数，则说明文件下载完毕，关闭文件，发生信号
        if( m_FinishedNum == m_DownloadCount )
        {
            m_File->flush();
            m_File->close();
            m_File->deleteLater();
            emit fileDownloadFinished();
            qDebug() << "Download finished";
            for(auto i : m_ThreadHadDownload)
            {
                qDebug() << i;
            }
            //线程全部退出
            for(auto i : m_ThreadList)
            {
                i->quit();
            }
            //清空文件
            QFile file("C:\\Users\\CHD\\Desktop\\download.txt");
            if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
                QTextStream stream(&file);
                stream << "";
                file.close();
            }
        }
}

void DownloadController::onConnected()
{
    qDebug() << "connect!";
    QByteArray recv = m_tcpSocket->readAll();
    qDebug() << recv;
}

void DownloadController::onError(QString errorMsg)
{
    emit error(errorMsg);
}

//开始下载任务
void DownloadController::onStartTask(QString url, int threadNum, int speed, QString userName, QString password)
{
        startFileDownload(url, threadNum, speed, userName, password);
        m_ThreadHadDownload = std::vector<int>(threadNum, 0);
        m_Status = t_Downloading;
}

//唤醒暂停线程任务
void DownloadController::onStopTask()
{
       qDebug() << "downloadController stop";
       emit stopThreadTask();
       m_Status = t_Pause;
}
//唤醒互斥条件
void DownloadController::onRestartTask()
{
        m_Status = t_Downloading;
        qDebug()<<m_HttpDownloaderList.size();
        for(auto i : m_HttpDownloaderList)
        {
            i->m_condition.wakeAll();
            //qDebug() <<i->m_Index<< "wakeup";
        }
        for(auto i : m_FtpDownloaderList)
        {
            i->m_condition.wakeAll();
            //qDebug() <<i->m_Index<< "wakeup";
        }
        //emit restartThreadTask();
}

//删除任务(结束所有线程)
void DownloadController::onDeleteTask()
{
    for(auto i : m_ThreadList)
    {
        if(i->isRunning()){
            i->quit();
        }
    }
}

//打印线程完成
void DownloadController::finshedThread()
{
    qDebug() << "end thread";
}


//更新已下载的字节大小
void DownloadController::updateCurrentSize(int index, int deltaSize)
{
    //std::lock_guard<std::mutex> lock(file_mutex); // 自动上锁
    m_nCurrentDownloadSize += deltaSize;
    //qDebug()<<"m_nCurrentDownloadSize"<<m_nCurrentDownloadSize;
    m_ThreadHadDownload[index - 1] += deltaSize;

}

